Skip to content

feat: node-gtk flatpak — package apps as installable Flatpaks#490

Merged
romgrk merged 3 commits into
bundle-clifrom
flatpak-cli
Jul 5, 2026
Merged

feat: node-gtk flatpak — package apps as installable Flatpaks#490
romgrk merged 3 commits into
bundle-clifrom
flatpak-cli

Conversation

@romgrk

@romgrk romgrk commented Jul 5, 2026

Copy link
Copy Markdown
Owner

What

Stacked on #489 (base: bundle-cli) — merge that first, then rebase this onto master.

The installable deliverable for real users:

cd my-app
npx node-gtk flatpak --install   # → dist/flatpak/MyApp.flatpak + installed
flatpak run com.example.MyApp

MyApp.flatpak is a single file users double-click to install through GNOME Software on any distro — it embeds the Flathub repo reference so the GNOME runtime is fetched automatically. The generated manifest is what a Flathub submission needs (docs cover the path + flatpak-builder-lint).

Verified end-to-end locally: built, installed, and ran a GTK4 smoke app under GNOME Platform 49 with the node26 SDK extension. The deliverable is 18 MB vs the same app's 100 MB portable tar.gz — GTK is shared via org.gnome.Platform, exactly the traction story.

How

  • Offline-build problem sidestepped: flatpak-builder builds without network, which normally forces lockfile→sources generators (weak for pnpm). Instead, the app-tree step from feat: ship node-gtk apps — bundle (portable) + flatpak (installable) #489 stages the app + production node_modules as a plain dir source. New rebuildAddon mode: node-gtk ships its compile inputs (src/, binding.gyp, nan) instead of a host-built binding.
  • Addon compiled in-sandbox against the runtime's GTK, using npm's internal node-gyp with --nodedir=/usr/lib/sdk/node<N> (the SDK extension bundles node headers — still offline) and explicit -Dmodule_name/-Dmodule_path (binding.gyp is node-pre-gyp style; its action_after_build then installs the binding where lib/native.js looks).
  • node at runtime: copied from org.freedesktop.Sdk.Extension.node<N> into /app/bin (the Platform doesn't ship node).
  • Generated files: manifest YAML, launcher, .desktop, AppStream metainfo (with explicit TODOs for Flathub review), icon install — all from the shared "bundle" config. New fields: summary, icon, license, categories, flatpak.{runtimeVersion,node,finishArgs}.
  • Sandbox defaults are minimal: wayland, fallback-x11, ipc, dri; network/filesystem are opt-in via finishArgs.
  • Builds with native flatpak-builder or org.flatpak.Builder (the flatpak-run variant gets --filesystem=<out> — its sandbox can't see /tmp).

Docs

doc/bundling.md restructured: Flatpak first (installable, Flathub path, sandbox permissions, the "flatpak id must equal your GApplication id" gotcha), portable bundles second, roadmap (win/mac/AppImage) unchanged.

Testing

  • Local end-to-end as above (build → bundle file → install → run).
  • scripts/flatpak-smoke-test.js in CI (ubuntu, node 24): generation-only — asserts manifest/desktop/metainfo/launcher correctness and that the staged sources are compilable (binding.gyp + src/ + nan present, no host binding leaked). The full sandbox build isn't run per-CI-run (downloads the ~1 GB GNOME SDK).
  • bundle smoke test still passes after the shared-config refactor.

Follow-ups

  • Flathub submission automation (release tarball source + manifest PR scaffold)
  • Optional full-build workflow_dispatch CI job with SDK caching
  • Windows/macOS bundle platform modules (unchanged roadmap)

🤖 Generated with Claude Code

romgrk and others added 3 commits July 5, 2026 01:27
The format real users install: one-click via GNOME Software, Flathub-
ready, sandboxed, with the GTK runtime shared across apps (the smoke
app's deliverable is an 18MB .flatpak vs its 100MB portable tar.gz).

GTK comes from org.gnome.Platform — nothing GTK-related is bundled.
flatpak-builder builds offline; instead of the usual lockfile→sources
generators, the same app-tree step `node-gtk bundle` uses stages the
app + production node_modules as a plain dir source (new rebuildAddon
mode: node-gtk ships its compile inputs — src/, binding.gyp, nan — and
no host binding). The addon is compiled in-sandbox against the
runtime's GTK with npm's internal node-gyp: --nodedir points at the
node SDK extension's bundled headers (offline), and module_name/
module_path are passed explicitly since binding.gyp is node-pre-gyp
style. The node binary is copied from the SDK extension into /app/bin.

Generates manifest + launcher + .desktop + AppStream metainfo + icon
install from the shared "bundle" config (new: summary, icon, license,
categories, flatpak.{runtimeVersion,node,finishArgs}), builds with
flatpak-builder or org.flatpak.Builder (granting the output dir to its
sandbox — it cannot see /tmp), and emits a single-file .flatpak via
build-bundle --runtime-repo so end-user installs fetch the Platform
from Flathub automatically.

Verified end-to-end locally: built, installed and ran the smoke app
under GNOME Platform 49 / node26 extension. CI checks generation only
(the sandbox build needs the ~1GB GNOME SDK).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Apps written with the `gi:` import scheme (the `node-gtk create`
default) crashed at startup in bundles and flatpaks with
ERR_UNSUPPORTED_ESM_URL_SCHEME: the loader hooks are installed by
`node --import node-gtk/register`, which the generated launchers only
passed when the app configured nodeArgs by hand.

Launchers now pass --import node-gtk/register by default; the new
`register: false` config key opts out. Harmless for CJS apps —
register.mjs only installs module hooks.

Verified: an ESM `import Gtk from 'gi:Gtk-4.0'` app built, installed
and ran as a flatpak; the CJS bundle smoke test still passes with the
flag present.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A rolling-release host can expose introspected names an older GNOME
runtime doesn't (glib 2.88 introspects g_unix_signal_add_full as
GLibUnix.signalAdd; the GNOME 49 runtime's glib 2.86 exposes
signalAddFull). Document the version-tolerant lookup pattern and the
one-liner to get a node REPL inside the sandbox.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@romgrk

romgrk commented Jul 5, 2026

Copy link
Copy Markdown
Owner Author

Consolidated into #489 per review preference — that PR now carries the full bundle+flatpak work.

@romgrk romgrk merged commit fef8c43 into bundle-cli Jul 5, 2026
@romgrk romgrk deleted the flatpak-cli branch July 5, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant